//
// Created by 33145 on 2023/1/14.
//

#ifndef SYLAR_SCHEDULER_H
#define SYLAR_SCHEDULER_H

#include <memory>
#include "sylar.h"
#include "hook.h"

namespace sylar {
  // 调度程序
  class fiber;
  class Scheduler {
  public:
    typedef std::shared_ptr<Scheduler> ptr;
    typedef Mutex MutexType;

    /// @param: use_caller 当前调用构造函数的线程是否要被纳入Scheduler
    Scheduler(size_t threads = 1, bool use_caller = true, const std::string& name = "");
    virtual ~Scheduler();

    const std::string& getName() const { return m_name; }

    static Scheduler* GetThis();
    // 每个协橙调度器都有一个主协橙
    static Fiber* GetMainFiber();
    // 开关线程池
    void start();
    void stop();

    template<class FiberOrCallback>
    void schedule(FiberOrCallback fc, int thread = -1) {
      bool need_tickle = false;
      {
        MutexType::Lock lock(m_mutex);
        need_tickle = scheduleNoLock(fc, thread);
      }
      if (need_tickle) {
        tickle();
      }
    }

    template<class InputIterator>
    void schedule(InputIterator begin, InputIterator end) {
      bool need_tickle = false;
      {
        // 保证任务的顺序是一定的，会连续执行
        MutexType::Lock lock(m_mutex);
        while (begin != end) {
          // 传入&他会把 begin 里的内容swap掉
          need_tickle = scheduleNoLock(&*begin) || need_tickle;
          ++begin;
        }
      }
      if (need_tickle) {
        tickle();
      }
    }
  protected:
    // 类似notify
    virtual void tickle();
    void run();
    /**
     * @brief: 如果当前正在停止、调度器已启动、任务队列已空、没有线程正在执行任务，则停止调度器
     * */
    virtual bool stopping();
    virtual void idle();     // 解决当前协橙没有任务但是线程又不能终止的问题，此时就可以执行该函数
    bool hasIdleThreads() { return m_idleThreadCount > 0; }
    void setThis();
  private:
    template<class FiberOrCallback>
    bool scheduleNoLock(FiberOrCallback fc, int thread) {
      bool need_tickle = m_fibers.empty();
      FiberAndThread ft(fc, thread);
      if (ft.fiber || ft.cb) {
        m_fibers.push_back(ft);
      }
      return need_tickle;
    }

  private:
    struct FiberAndThread {
      sylar::Fiber::ptr fiber;
      std::function<void()> cb;
      int threadId;
      // 这里两个构造函数可能是想，，在合适的情况下调用合适的，即有时候我们不想让他使用右值形式的构造
      FiberAndThread(Fiber::ptr f, int thr) : fiber(f), threadId(thr) {}

      FiberAndThread(Fiber::ptr* f, int thr) : threadId(thr) {
        fiber.swap(*f);
      }
      FiberAndThread(std::function<void()> f, int thr) : cb(f), threadId(thr) {}

      FiberAndThread(std::function<void()>* f, int thr) : threadId(thr) {
        cb.swap(*f);
      }

      FiberAndThread() : threadId(-1) {}

      void reset() {
        fiber = nullptr;
        cb = nullptr;
        threadId = -1;
      }
    };
  private:
    MutexType m_mutex;
    std::vector<sylar::Thread::ptr> m_threads;
    std::list<FiberAndThread> m_fibers;  // 一些计划要执行的协程（协橙，回调函数。。）
    // std::map<int, std::list<FiberAndThread>> m_thrFibers;  // key：线程号  value：要在该线程内执行的任务
    std::string m_name;
    Fiber::ptr m_root_fiber;    // 主协橙

  protected:
    // 用于随机访问一个线程？
    std::vector<int> m_threadIds;
    std::atomic<size_t> m_threadCount = {0};        // 线程总数
    std::atomic<size_t> m_activeThreadCount = {0};  // 活跃线程数量
    std::atomic<size_t> m_idleThreadCount = {0};    // 空闲线程数量
    int m_rootThreadId = 0;                         // 主线程ID
    bool m_stopping = true;                         // 表示状态：未启动/已启动（true/false）
    bool m_autostop = false;                        // 表示状态：停止中/已停止（true/false）
  };
}

#endif //SYLAR_SCHEDULER_H
